#ifndef SERVER_USER_SERVER_USER_H
#define SERVER_USER_SERVER_USER_H

#include <brpc/server.h>
#include <butil/logging.h>

#include <memory>
#include <unordered_map>
#include <unordered_set>

#include "channel.h"
#include "data_es.h"
#include "etcd.h"
#include "base.pb.h"
#include "friend.pb.h"
#include "mysql_friend_request.h"
#include "mysql_relationship.h"
#include "mysql_session.h"
#include "mysql_session_info.h"

namespace chat {
class FriendServiceImpl : public FriendService {
   public:
    FriendServiceImpl(const std::shared_ptr<elasticlient::Client>& es_client,
                      const std::shared_ptr<odb::mysql::database>& odb_client,
                      const ServiceManagerPtr service_manager,
                      const std::string& user_service_name,
                      const std::string& message_service_name)
        : odb_friend_request_(std::make_shared<FriendRequestTable>(odb_client)),
          odb_relationship_(std::make_shared<RelationshipTable>(odb_client)),
          odb_session_info_(std::make_shared<ChatSessionInfoTable>(odb_client)),
          odb_session_member_(
              std::make_shared<ChatSessionMemberTable>(odb_client)),
          es_user_(std::make_shared<ESUser>(es_client)),
          service_manager_(service_manager),
          user_service_name_(user_service_name),
          message_service_name_(message_service_name) {}

    void GetFriendList(::google::protobuf::RpcController* controller,
                       const ::chat::GetFriendListReq* request,
                       ::chat::GetFriendListRsp* response,
                       ::google::protobuf::Closure* done);
    void FriendRemove(::google::protobuf::RpcController* controller,
                      const ::chat::FriendRemoveReq* request,
                      ::chat::FriendRemoveRsp* response,
                      ::google::protobuf::Closure* done);
    void FriendAdd(::google::protobuf::RpcController* controller,
                   const ::chat::FriendAddReq* request,
                   ::chat::FriendAddRsp* response,
                   ::google::protobuf::Closure* done);
    void FriendAddProcess(::google::protobuf::RpcController* controller,
                          const ::chat::FriendAddProcessReq* request,
                          ::chat::FriendAddProcessRsp* response,
                          ::google::protobuf::Closure* done);

    /**
     * @note: 该函数用于根据请求中的关键字搜索陌生人
     */
    void FriendSearch(::google::protobuf::RpcController* controller,
                      const ::chat::FriendSearchReq* request,
                      ::chat::FriendSearchRsp* response,
                      ::google::protobuf::Closure* done);
    void GetChatSessionList(::google::protobuf::RpcController* controller,
                            const ::chat::GetChatSessionListReq* request,
                            ::chat::GetChatSessionListRsp* response,
                            ::google::protobuf::Closure* done);

    /**
     * @note:
     * 该函数主要用于群聊会话的创建，对于单聊会话，好友添加完成后就会自动创建
     */
    void ChatSessionCreate(::google::protobuf::RpcController* controller,
                           const ::chat::ChatSessionCreateReq* request,
                           ::chat::ChatSessionCreateRsp* response,
                           ::google::protobuf::Closure* done);
    void GetChatSessionMember(::google::protobuf::RpcController* controller,
                              const ::chat::GetChatSessionMemberReq* request,
                              ::chat::GetChatSessionMemberRsp* response,
                              ::google::protobuf::Closure* done);
    void GetPendingFriendEventList(
        ::google::protobuf::RpcController* controller,
        const ::chat::GetPendingFriendEventListReq* request,
        ::chat::GetPendingFriendEventListRsp* response,
        ::google::protobuf::Closure* done);

   private:
    bool getUsersInfo(
        const std::unordered_set<std::string>& user_ids,
        std::unordered_map<std::string, chat::UserInfo>* user_infos);

    bool getRecentOneMessage(const std::string& chat_session_id,
                             MessageInfo* message);

   private:
    FriendRequestTablePtr odb_friend_request_ = nullptr;
    RelationshipTablePtr odb_relationship_ = nullptr;
    ChatSessionInfoTablePtr odb_session_info_ = nullptr;
    ChatSessionMemberTablePtr odb_session_member_ = nullptr;

    ESUserPtr es_user_ = nullptr;

    ServiceManagerPtr service_manager_ = nullptr;
    std::string user_service_name_;
    std::string message_service_name_;
};

class FriendServer {
   public:
    FriendServer(const RegistryPtr& register_client,
                 const DiscoveryPtr& discovery_client,
                 const std::shared_ptr<brpc::Server> brpc_server,
                 const std::shared_ptr<elasticlient::Client>& es_client,
                 const std::shared_ptr<odb::mysql::database> odb_client)
        : register_client_(register_client),
          discovery_client_(discovery_client),
          brpc_server_(brpc_server),
          es_client_(es_client),
          odb_client_(odb_client) {}

    /**
     * @brief 启动rpc服务
     */
    void start() { brpc_server_->RunUntilAskedToQuit(); }

   private:
    RegistryPtr register_client_ = nullptr;
    DiscoveryPtr discovery_client_ = nullptr;

    std::shared_ptr<brpc::Server> brpc_server_ = nullptr;

    std::shared_ptr<elasticlient::Client> es_client_ = nullptr;
    std::shared_ptr<odb::mysql::database> odb_client_ = nullptr;
};

using FriendServerPtr = std::shared_ptr<FriendServer>;

class FriendServerBuilder {
   public:
    /**
     * @brief 创建服务注册客户端
     * @param register_name[in]: 服务名称
     * @param etcd_host[in]: etcd服务器 ip:port
     * @param access_host[in]: 外部访问 ip:port
     */
    void makeRegisterClient(const std::string& register_name,
                            const std::string& etcd_host,
                            const std::string& access_host);

    /**
     * @brief 创建rpc服务管理对象
     *
     * @param user_service_name[in]: 要关心的用户管理服务名称
     * @param message_service_name[in]: 要关心的消息存储服务名称
     */
    void makeServiceManager(const std::string& user_service_name,
                            const std::string& message_service_name);

    /**
     * @brief 创建服务发现客户端
     * @param etcd_host[in]: etcd服务器 ip:port
     * @param base_service[in]: 服务发现时的根目录
     */
    void makeDiscoveryClient(const std::string& etcd_host,
                             const std::string& base_service);

    /**
     * @brief 创建 brpc 服务
     * @param port[in]: brpc 服务器端口
     * @param idle_timeout_sec[in]: 连接空闲超时时间，超时后连接关闭
     * @param num_threads[in]: 线程数量
     */
    void makeBrpcServer(const uint16_t port, const int idle_timeout_sec,
                        const int num_threads);

    /**
     * @brief 创建 ODB 客户端
     *
     * @param max_pool[in]: 最大连接池数量
     * @param user[in]: mysql用户名
     * @param password[in]: 对应用户名密码
     * @param db[in]: 要链接到的数据库名称
     * @param ip[in]: mysql服务器ip
     * @param port[in]: mysql服务器端口
     * @param charset[in]: 所采用的字符集
     */
    void makeOdbClient(const int max_pool, const std::string& user,
                       const std::string& password, const std::string& db,
                       const std::string& ip, const uint16_t port,
                       const std::string& charset);

    /**
     * @brief 创建ES客户端
     */
    void makeEsClient(const std::vector<std::string> host_list);

    /**
     * @brief 构造一个 FriendServer对象 并返回
     */
    FriendServerPtr build();

   private:
    RegistryPtr register_client_ = nullptr;

    ServiceManagerPtr service_manager_ = nullptr;
    DiscoveryPtr discovery_client_ = nullptr;
    std::string user_service_name_;
    std::string message_service_name_;

    std::shared_ptr<brpc::Server> brpc_server_ = nullptr;

    std::shared_ptr<odb::mysql::database> odb_client_ = nullptr;
    std::shared_ptr<elasticlient::Client> es_client_ = nullptr;
};
}  // namespace chat

#endif  // SERVER_USER_SERVER_USER_H